#include "include/lrms.h"
#include "include/protocol.h"

/* =============================================================== *
 *			Prototypes				   *
 * =============================================================== */

int create_udp_socket(int * sockfd);
int connect_to_scheduler(int sockClient, char * ip_addr, int portNum, struct sockaddr_in * addrSched);
int send_program(int sockClient, struct sockaddr_in  addrSched, char * program_name, char * argument[], int nbrArg);
int wait_for_results(int sockClient, struct sockaddr_in addrSched, char * results_file_name);

/* =============================================================== *
 *			Globals Variables			   *
 * =============================================================== */

static int log_file = 0;	// log file
static char log_msg[MAXLOGMSG];


/* =============================================================== *
 *			MAIN FUNCTION				   *
 * =============================================================== */

int main(int argc, char* argv[])
{
	int sockClient;				// socket client
	struct sockaddr_in addrSched;		// Scheduler address
	char results_file_name[MAXNAME];	// results uotput file name
	int returnStatus;			// for each function call
	
	/* ----------------------- get arguments ----------------------- */
	
	if (argc < 4)
	{
		sprintf( log_msg, "Usage: %s <Scheduler ip> <port> <filename> [ < arg 1> ..... ].\n", argv[0]);
		print_msg( log_msg, 1, 1, 0, log_file);
		exit(EXIT_FAILURE);
	}
	
	
	/* ------------------------ open log file ---------------------- */
	
	log_file = open( "client.log", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR );
	
	
	/* --------------------create client socket -------------------- */
	
	returnStatus = create_udp_socket ( &sockClient);
	if ( returnStatus != EXIT_SUCCESS)
	{
		sprintf( log_msg, "Client::Create_UDP_Socket : Error : Socket creation.\n");
		print_msg( log_msg, 0, 1, 1, log_file);
		exit(EXIT_FAILURE);
	}
	sprintf( log_msg, "Client::Create_UDP_Socket : Socket %d created successfully .\n", sockClient);
	print_msg( log_msg, 1, 0, 1, log_file);


	/* ------------------connect to the Scheduler -------------------*/
	
	returnStatus = connect_to_scheduler( sockClient, argv[1], atoi(argv[2]), &addrSched);
	if ( returnStatus != EXIT_SUCCESS)
	{
		sprintf( log_msg, "Client::Connect_To_Scheduler : Error : Cann't connect to the scheduler.\n");
		print_msg( log_msg, 0, 1, 1, log_file);
		exit(EXIT_FAILURE);
	}
	sprintf( log_msg, "Client::Connect_To_Scheduler : Connected to Scheduler %s@%d.\n", inet_ntoa(addrSched.sin_addr), ntohs(addrSched.sin_port));
	print_msg( log_msg, 1, 0, 1, log_file);
	
	
	/* ------------------Send program to the server ----------------*/
	
	returnStatus = send_program( sockClient, addrSched, argv[3], argv+4, argc-4);
	if ( returnStatus != EXIT_SUCCESS)
	{
		sprintf( log_msg, "Client::Send_Program : Error : Cann't Send program to the scheduler.\n");
		print_msg( log_msg, 0, 1, 1, log_file);
		exit(EXIT_FAILURE);
	}
	sprintf( log_msg, "Client::Send_Program : Program sended to %s@%d.\n", inet_ntoa(addrSched.sin_addr), ntohs(addrSched.sin_port));
	print_msg( log_msg, 1, 0, 1, log_file);
	
	
	/* ---------------------- wait for results ---------------------*/
	
	strcpy( results_file_name, argv[3]);
	strcat( results_file_name, ".results");

	returnStatus = wait_for_results(sockClient, addrSched, results_file_name);
	if ( returnStatus != EXIT_SUCCESS)
	{
		sprintf( log_msg, "Client::Wait_For_Results : Error : Could not receive results.\n");
		print_msg( log_msg, 0, 1, 1, log_file);
		exit(EXIT_FAILURE);
	}
	sprintf( log_msg, "Client::Wait_For_Results : Results has been received : Check %s file.\n", results_file_name);
	print_msg( log_msg, 1, 0, 1, log_file);


	/* -------------------------- cleanup ------------------------- */
	close(log_file);
	close(sockClient);
	return EXIT_SUCCESS;
}


/* =============================================================== *
 *		       CREATE UDP SOCKET			   *
 * =============================================================== */

int create_udp_socket(int * sockfd)
{
	*sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (*sockfd < 0)
	{
		perror("socket");
		return EXIT_FAILURE;
	}
	
	return EXIT_SUCCESS;
}


/* =============================================================== *
 *		       CONNECT TO SCHEDULER			   *
 * =============================================================== */

int connect_to_scheduler(int sockClient, char * addr, int portNum, struct sockaddr_in * addrSched)
{
	char bufctrl[MAXBUFCTRL];
	int returnStatus = 0;
	void * pReturnStatus = NULL;
	socklen_t addrlen;			// size of sockaddr_in
	
	/* init the addrSched */
	pReturnStatus = memset(addrSched, 0, sizeof(struct sockaddr_in));
	if( pReturnStatus == NULL) {
		perror("memset");
		return EXIT_FAILURE;
	}
	
	
	/* construct the addrSched */
	addrSched->sin_family = AF_INET;
	(addrSched->sin_addr).s_addr = inet_addr(addr);
	addrSched->sin_port = htons(portNum);
	
	
	/* send request to the scheduler */
	strcpy(bufctrl, dem);	
	returnStatus = sendto(sockClient, bufctrl, strlen(dem), 0,(SA*) addrSched, sizeof(*addrSched));
	if (returnStatus < 0) 
	{
		perror("sendto");
		return EXIT_FAILURE;
	}
	
	//fprintf(stdout, "Client::Connect_To_Scheduler : Wait for response from %s@%d.\n", inet_ntoa(addrSched->sin_addr), ntohs(addrSched->sin_port));
	
	
	/* receive acknowledge from scheduler */
	do {
		addrlen = sizeof(*addrSched);
		returnStatus = recvfrom(sockClient, bufctrl, returnStatus, 0,(SA*)addrSched, &addrlen);
		if (returnStatus < 0) 
		{
			perror("recvfrom");
			sprintf( log_msg, "Could not receive acknowledge (%s).\n",dem);
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE; 
		}
		
		bufctrl[returnStatus] = '\0';
		
	} while( strcmp(bufctrl,dem) != 0 );

	return EXIT_SUCCESS;
	
}


/* =============================================================== *
 *		          SEND PROGRAM				   *
 * =============================================================== */

int send_program(int sockClient, struct sockaddr_in  addrSched, char * program_name, char * argument[], int nbrArg)
{
	char bufctrl[MAXBUFCTRL];
	char buffile[MAXBUFFILE];
	socklen_t addrlen;			// size of sockaddr_in
	int readCounter = 0;
	int returnStatus = 0;
	int program_fd = 0;
	int i;
	
	
	/* open file (program) for reading */ 
	program_fd = open(program_name, O_RDONLY,  S_IRUSR);
	if(program_fd < 0) 
	{
	 	perror("open");
	 	
	 	return EXIT_FAILURE;
	}
	
	
	/* send file(program) to scheduler */
	while((readCounter = read(program_fd, buffile, MAXBUFFILE)) > 0 )
	{
		returnStatus = sendto(sockClient, buffile, readCounter, 0, (SA*)&addrSched, sizeof(addrSched));
		if (returnStatus < 0) 
		{
			perror("sendto");
			sprintf( log_msg, "Could not send file.\n");
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
	}
	
	
	/* send finish message (progterm)*/
	strcpy(bufctrl, progterm);
	returnStatus = sendto(sockClient, bufctrl, strlen(progterm), 0,(SA*)&addrSched, sizeof(addrSched));
	if (returnStatus < 0) 
	{
		perror("sendto");
		sprintf( log_msg, "Could not send %s message.\n", progterm);
		print_msg( log_msg, 0, 1, 1, log_file);
		return EXIT_FAILURE;
	}
	
	
	/* Sending arguments */
	for(i=0; i<nbrArg; i++)
	{
		strcpy(buffile, argument[i]);
		returnStatus = sendto(sockClient, buffile, sizeof(buffile), 0,(SA*)&addrSched, sizeof(addrSched));
		if (returnStatus < 0) 
		{
			perror("sendto");
			sprintf( log_msg, "Could not send arguments.\n");
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}	
	}
	
	
	/* send argterm */	
	strcpy(bufctrl, argterm);
	returnStatus = sendto(sockClient, bufctrl, sizeof(argterm), 0, (SA*)&addrSched, sizeof(addrSched));
	if (returnStatus < 0) 
	{
		perror("sendto");
		sprintf( log_msg, "Could not send argterm (%s).\n", argterm);
		print_msg( log_msg, 0, 1, 1, log_file);
		return EXIT_FAILURE;
	}
	
	close(program_fd);
	
	return EXIT_SUCCESS;
}


/* =============================================================== *
 *		        WAIT FOR RESULTS			   *
 * =============================================================== */

int wait_for_results(int sockClient, struct sockaddr_in addrSched, char * results_file_name)
{
	char buffile[MAXBUFFILE];
	socklen_t addrlen;			// size of sockaddr_in
	int returnStatus = 0;
	int results_file_fd = 0;
	
	/* open file  for writing */ 
	results_file_fd = open(results_file_name, O_WRONLY | O_CREAT /*| O_TRUNC*/, S_IRUSR | S_IWUSR );
	if(results_file_fd < 0) 	
	{
	 	perror("open");
	 	sprintf( log_msg, "Could not create file for saving results.\n");
	 	print_msg( log_msg, 0, 1, 1, log_file);
	 	return EXIT_FAILURE;
	}
	
	while(1)
	{
		/* receive results */
		addrlen = sizeof(addrSched);
		returnStatus = recvfrom( sockClient, buffile, MAXBUFFILE, 0,(SA*)&addrSched, &addrlen);
		if (returnStatus < 0) 
		{
			perror("recvfrom");
			sprintf( log_msg, "Could not receive results.\n");
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		// plus
		buffile[returnStatus] = '\0';
		
		if(strcmp(buffile, resultsterm) == 0) break;
		
		/* write to file */
		returnStatus = write(results_file_fd, buffile, returnStatus);
		if (returnStatus < 0) 
		{
			perror("write");
			sprintf( log_msg, "Could not write results to file.\n");
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		/* print results */
		//fprintf(stdout, "%s",buffile);
	}
	
	//fprintf(stdout, "\n");
	close(results_file_fd);
	
	return EXIT_SUCCESS;
}


